using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;

using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using System.Configuration;
using System.IO;

namespace Musicman.Common
{
    /// <summary>
    /// Class used to log info and exception messages
    /// </summary>
    public static class LogManager
    {
        public const string FlatFileWithoutRethrowPolicy = "FlatFileWithoutRethrowing";
        public const string FlatFileWithRethrowPolicy = "FlatFileWithRethrowing";

        private static LogWriter writer;
        private static LoggingSettings logConfig;

        public static void InitializeLogWriter()
        {
            LogWriterFactory factory = new LogWriterFactory();
            writer = factory.Create();
        }

        /// <summary>
        /// Logging messages in info.log file (timestamp & message)
        /// </summary>
        /// <param name="info"></param>           
        public static void LogInfo(string info)
        {
            LogEntry logEntry = new LogEntry();
            logEntry.Message = info;
            logEntry.Categories.Add(Constants.LogTypes.Info.ToString());

            if (writer == null)
            {
                InitializeLogWriter();
            }

            writer.Write(logEntry);
        }

        /// <summary>
        /// Logging messages in exception.log file (trace info and message)
        /// After logging same exception is thrown again
        /// </summary>
        /// <param name="exceptionToCatch"></param>
        public static void LogExceptionThrow(Exception exceptionToCatch)
        {
            LogException(exceptionToCatch, FlatFileWithRethrowPolicy);
        }

        /// <summary>
        /// Logging messages in exception.log file (trace info and message)
        /// </summary>
        /// <param name="exceptionToCatch"></param>
        public static void LogException(Exception exceptionToCatch)
        {
            LogException(exceptionToCatch, FlatFileWithoutRethrowPolicy);
        }


        /// <summary>
        /// Logging messages in exception.log file (trace info and message) 
        /// and sending mail with exception message to mail adress - if sending mail is enabled
        /// </summary>
        /// <param name="exceptionToCatch"></param>
        /// <param name="policy"></param>
        private static void LogException(Exception exceptionToCatch, string policy)
        {
            // string subject = "Exception";
            string messageBody = "Message: " + exceptionToCatch.Message;
            messageBody += "\n\n\nStack Trace: \n\n" + exceptionToCatch.StackTrace;
            if (exceptionToCatch.InnerException != null)
            {
                messageBody += "\n\nMessage: " + exceptionToCatch.InnerException.Message;
                messageBody += "\n\n\nInner Stack Trace: \n\n" + exceptionToCatch.InnerException.StackTrace;
            }

            /* mail sending of exceptions 
             * for later implementation
            if (AppSettingsHelper.IsEnabledSendingMail())
            {
                Mailer.SendMail(subject, messageBody);
            }
            */

            bool rethrow = ExceptionPolicy.HandleException(exceptionToCatch, policy);
            if (rethrow)
            {
                throw exceptionToCatch;
            }
        }

        /// <summary>
        /// Logging messages in exception.log file (trace info and message)
        /// Logs common explanation for exception and original exception as inner exception
        /// </summary>
        /// <param name="exceptionToCatch"></param>
        public static void LogException(String exceptionExplanation, Exception exceptionToCatch)
        {
            Exception detExc = new Exception(exceptionExplanation, exceptionToCatch);
            LogException(detExc, FlatFileWithoutRethrowPolicy);
        }

        #region Delete Logs

        private static bool BackupFile(FileInfo file, ZipOutputStream zipStream)
        {
            if (file != null)
            {
                if (file.Exists)
                {
                    try
                    {
                        if (writer != null)
                        {
                            writer.Dispose();
                        }

                        FileStream fs = File.OpenRead(file.FullName);

                        byte[] buffer = new byte[fs.Length];

                        fs.Read(buffer, 0, buffer.Length);
                        fs.Close();

                        ZipEntry ze = new ZipEntry(file.Name);
                        zipStream.PutNextEntry(ze);
                        zipStream.Write(buffer, 0, buffer.Length);
                    }
                    catch (Exception err)
                    {
                        LogException("Can't bckup file" + file.FullName + ", it's used by another process", err);

                        return false;
                    }
                }
            }
            return true;
        }

        private static bool BackupLog(string logType, ZipOutputStream zipStream)
        {
            List<FileInfo> fileInfos = GetFileInfos(logType);

            if (fileInfos == null)
            {
                return false;
            }
            bool retVal = true;

            foreach (FileInfo info in fileInfos)
            {
                if (!BackupFile(info, zipStream))
                {
                    retVal = false;
                }
            }
            return retVal;
        }

        private static bool CreateBackup()
        {
            bool retval = true;

            StringBuilder name;
            string path;

            DirectoryInfo bckupDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\Backup");

            if (!bckupDir.Exists)
            {
                try
                {
                    bckupDir.Create();
                }
                catch (Exception err)
                {
                    LogManager.LogException("Failed to create BackUp Folder: " + bckupDir, err);
                    return false;
                }
            }

            try
            {
                name = new StringBuilder();
                name.Append(DateTime.Now.Day);
                name.Append(DateTime.Now.Month);
                name.Append(DateTime.Now.Year);
                name.Append(" - ");
                name.Append(DateTime.Now.Hour);
                name.Append(DateTime.Now.Minute);
                name.Append(DateTime.Now.Second);

                path = bckupDir.FullName + "\\" + name.ToString() + ".zip";
                ZipOutputStream zipStream = new ZipOutputStream(File.Create(path));
                zipStream.SetLevel(9);

                foreach (Constants.LogTypes value in Enum.GetValues(typeof(Constants.LogTypes)))
                {
                    if (!BackupLog(value.ToString(), zipStream))
                    {
                        retval = false;
                        break;
                    }
                }

                if (zipStream.Length == 0)
                {
                    retval = false;
                }

                zipStream.Flush();
                zipStream.Finish();
                zipStream.Close();
                zipStream.Dispose();

                if (!retval)
                {
                    File.Delete(path);
                }
            }
            catch (Exception err)
            {
                LogException("Error during creating bckup of logs", err);
                return false;
            }

            return retval;
        }

        public static bool BackupLogs()
        {
            if (CreateBackup())
            {
                return DeleteAllLogs();
            }

            return false;
        }

        public static bool DeleteAllLogs()
        {
            bool retVal = true;

            foreach (Constants.LogTypes value in Enum.GetValues(typeof(Constants.LogTypes)))
            {
                if (!DeleteLog(value.ToString()))
                {
                    retVal = false;
                }
            }

            return retVal;
        }

        public static bool DeleteInfoLog()
        {
            return DeleteLog(Constants.LogTypes.Info.ToString());
        }

        private static bool DeleteLog(string logType)
        {
            List<FileInfo> fileInfos = GetFileInfos(logType);
            return DeleteFiles(fileInfos);
        }

        private static bool DeleteFiles(List<FileInfo> fileInfos)
        {
            if (fileInfos == null)
            {
                return false;
            }
            bool retVal = true;

            foreach (FileInfo info in fileInfos)
            {
                if (!DeleteFile(info))
                {
                    retVal = false;
                }
            }

            return retVal;
        }

        private static bool DeleteFile(FileInfo file)
        {
            if (file != null)
            {
                if (file.Exists)
                {
                    try
                    {
                        if (writer != null)
                        {
                            writer.Dispose();
                            writer = null;
                        }
                        File.Delete(file.FullName);

                    }
                    catch (Exception err)
                    {
                        LogException("Can't delete file" + file.FullName + ", it's used by another process", err);

                        return false;
                    }
                }
            }
            return true;
        }

        private static List<FileInfo> GetFileInfos(string logType)
        {
            if (String.IsNullOrEmpty(logType))
            {
                return null;
            }
            if (logConfig == null)
            {
                logConfig = ConfigurationManager.GetSection(Constants.sloggingConfiguration) as LoggingSettings;
            }
            if (logConfig == null)
            {
                return null;
            }

            TraceSourceData ts = logConfig.TraceSources.Get(logType);

            if (ts == null)
            {
                return null;
            }

            string path;
            string filePath;

            List<FileInfo> retVal = new List<FileInfo>();

            foreach (TraceListenerReferenceData td in ts.TraceListeners)
            {
                path = td.ElementInformation.Source;

                if (td.ElementInformation.Properties[Constants.sName] != null)
                {
                    TraceListenerData tld = logConfig.TraceListeners.Get(td.ElementInformation.Properties[Constants.sName].Value.ToString());

                    if (tld.ElementInformation.Properties[Constants.sFileName] != null)
                    {
                        filePath = tld.ElementInformation.Properties[Constants.sFileName].Value.ToString();

                        if (path.LastIndexOf(@"\") == -1 || filePath.IndexOf(":\\") == 1)
                        {
                            path = filePath;
                        }
                        else
                        {
                            path = path.Remove(path.LastIndexOf(@"\") + 1);
                            path += filePath;
                        }
                        try
                        {
                            FileInfo fi = new FileInfo(path);
                            retVal.Add(fi);
                        }
                        catch (Exception err)
                        {
                            LogException("Error while trying to get file info, path:" + path, err);
                        }
                    }
                }
            }
            return retVal;
        }
        #endregion
    }
}
